#include "unione_lite.h"
#include "interrupt.h"
#include "DrvUART010.h"
#include "timer.h"

#define TMR_INPUT_CLK  				12288000
#define msec_to_jiffies(msec)     	(msec*(TMR_INPUT_CLK/1000))
#define usec_to_jiffies(usec)     	(usec*(TMR_INPUT_CLK/1000/1000))

//static u32 pre_fttmr_tick=0;
//static u64 fttmr_overflow_count=0; //not always correct, if it isn't accessed over one overflow period
static u32 ulTickCounter = 0;
static u32 ulTickCounter1 = 0;
static u32 ulTickCounter2 = 0;

static void  (*s_timer2_cb)(int TimerIdx) = NULL;
static void  (*s_timer3_cb)(int TimerIdx) = NULL;

extern void rt_tick_increase(void);

/*This function loads the initial value for time(id).*/
static void timer_load_value(u32 id, u32 value)
{
	WRITE(TMR_LOAD(id), value);
	WRITE(TMR_CNTER(id), value);
	return;
}
#if 0
/*This function gets current counter value for timer(id).*/
static u32 timer_get_value(u32 id)
{
    return READ(TMR_CNTER(id));
}
#endif

static void timer_clear_int_status(u32 id)
{
    int status = READ(TMR_INT_STATUS);
    status &= (0x7 << (3 * id));
    WRITE(TMR_INT_STATUS, status);
	return;
}

/*
This function sets the control bits for timer(id).
int_en: 
0: timer(id) interrupt is enabled
1: timer(id) interrupt is disabled

start: 
0: stop the timer(id)
1: start the timer(id)
*/
void timer_ctrl(u32 id, u32 int_en, u32 start)
{
    u32 ctrl = READ(TMR_CTRL);
    u32 mask = 0;
    u32 tmp = 0;

    tmp |= ((!!int_en) << 2);
    tmp |= ((!!start) << 0);
    tmp <<= (3 * id);
    
    tmp|= FTTMR_TM1_CLOCK; //ext1clk
	
	if(id == 1)
		tmp|= FTTMR_TM2_CLOCK;
	else if(id == 2)
	    tmp|= FTTMR_TM3_CLOCK;

    ctrl &= ~(7 << (3 * id));
    ctrl |= tmp;
 
    WRITE(TMR_CTRL, ctrl);
    mask = READ(TMR_INT_MASK);
    mask |= (7 << (3 * id));
    if(int_en){
        mask &= ~(4 << (3 * id));
    }
    WRITE(TMR_INT_MASK, mask);
    timer_clear_int_status(id); //clear timer interrupts
	return;
}

static int timer_interrupt_for_os(void)
{
    u32 load_cnt = READ(TMR_LOAD(TIMER1));
    
    if((ulTickCounter - 1) != load_cnt){
        timer_ctrl(TIMER1, 0, 0); //disable timer
        timer_load_value(TIMER1, ulTickCounter - 1);
        timer_ctrl(TIMER1, 1, 1); //enable timer
    }
	rt_tick_increase();
    return 0;
}

static void timer_interrupt(void)
{
    int int_status;
    int status = READ(TMR_INT_STATUS);
    int enable_bits = ~(READ(TMR_INT_MASK));
    int_status = status & enable_bits;
    if(int_status & (0x7 << 0)){ //TMR1
        timer_interrupt_for_os();
    }

    if(int_status & (0x7 << (3 * TIMER2))){ //TMR2
	    s_timer2_cb(TIMER2);
    }

    if(int_status & (0x7 << (3 * TIMER3))){ //TMR3
	    s_timer3_cb(TIMER3);
    }

    WRITE(TMR_INT_STATUS, status); //clear timer interrupt    
	return;
}

int timer_prepare_for_os(u32 tick_hz)
{
    ulTickCounter = TMR_CLOCK_HZ / tick_hz;

    timer_ctrl(TIMER1, 0, 0); //disable timer
    timer_load_value(TIMER1, ulTickCounter - 1);
    
    if(fLib_ConnectInt(TIMER_FTTMR010_ALL_IRQ, (PrVoid)timer_interrupt) == false){
        fLib_printf("Fail to register timer irq\n");		
    }	
    fLib_EnableInt(TIMER_FTTMR010_ALL_IRQ); 
    timer_ctrl(TIMER1, 1, 1); //enable timer
    return 0;
}

int enable_timer(int TimerIdx)
{
   	//u32 reg; 
    //reg = READ(TMR_CTRL);
	if(TimerIdx == TIMER2){	
        timer_ctrl(TIMER2, 0, 0); //disable timer
        timer_load_value(TIMER2, ulTickCounter1 - 1);
		timer_ctrl(TIMER2, 1, 1); //enable timer
	}else if(TimerIdx == TIMER3){
		timer_ctrl(TIMER3, 0, 0); //disable timer
        timer_load_value(TIMER3, ulTickCounter2 - 1);
		timer_ctrl(TIMER3, 1, 1); //enable timer
    }else{
		return -1;
	}
	return 0;
}

int disable_timer(int TimerIdx)
{    
    //u32 reg;
	if(TimerIdx == TIMER2){
		timer_ctrl(TIMER2, 0, 0); //disable timer
	}else if(TimerIdx == TIMER3){
		timer_ctrl(TIMER3, 0, 0); //disable timer
    }else{
		return -1;
	}
	return 0;
}

int timer_Config(int TimerIdx,u32 usec,void  (*timer_cb)(int TimerIdx))
{
	if(TimerIdx == TIMER2){
         ulTickCounter1 = ((u64)usec)*TMR_INPUT_CLK /1000000;
		 s_timer2_cb = timer_cb;
	}else if(TimerIdx == TIMER3){
         ulTickCounter2 = ((u64)usec)*TMR_INPUT_CLK /1000000;
		 s_timer3_cb = timer_cb;
    }else{
		return -1;
	}
	return 0;
}
#if 0
void init_jiffies_timer(void)
{
    unsigned int reg;
	timer_load_value(TIMER3,0xffffffff);
	WRITE(TMR_MATCH1(TIMER3), 0);
	WRITE(TMR_MATCH2(TIMER3), 0);
	reg = READ(TMR_INT_MASK);
	reg |= (FTTMR_TM3_MATCH1 | FTTMR_TM3_MATCH2 | FTTMR_TM3_OVERFLOW);
	WRITE(TMR_INT_MASK,reg);
	reg = READ(TMR_CTRL);
	reg |= (FTTMR_TM3_ENABLE | FTTMR_TM3_CLOCK);//ext3clk
	WRITE(TMR_CTRL,reg);
	return;
}

UINT64 jiffies(void)
{
    u64  global_ticks;
	u32	fttmr_ticks = 0xffffffff - READ(TMR_CNTER(TIMER3));;	/* note that we use a count-down timer */

    if (pre_fttmr_tick > fttmr_ticks) {
        fttmr_overflow_count ++;
    }
    global_ticks =  (fttmr_overflow_count << 32) + fttmr_ticks;
    pre_fttmr_tick = fttmr_ticks;
	return global_ticks;
}


void __mdelay(unsigned long msec)
{
    unsigned long long start;
    unsigned tmo;
    start = jiffies();        /* get current timestamp */
    tmo = msec_to_jiffies(msec);   /* convert usecs to ticks */
    while ((jiffies() - start) < tmo) ; /* loop till time has passed */
	return;
}

//ycmo: adaptable for u-boot
void udelay(unsigned long usec)
{
   // __mdelay(usec/1000);
   
    u64 start;
    u32 tmo;
    start = jiffies();        /* get current timestamp */
    tmo = usec_to_jiffies(usec);   /* convert usecs to ticks */
    while ((jiffies() - start) < tmo) ;           /* loop till time has passed */
	return;
}
#endif
